﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using GTServices.Model.CoRoutine;
using Caliburn.Micro;
using GTServices.Entities;
using GTServices.Model.Extensions;
using System.Collections.Specialized;
using System.Text.RegularExpressions;


namespace GTServices.Model.Models
{
    public class FileManagerModel
    {
        public ICollection<FileInfo> FileInfos { get; set; }
        
        public List<String> ContainingPaths
        {
            get
            {
                var paths = FileInfos.Select((x) => x.Path).Distinct();
                return paths.ToList();
            }
        }
        public bool HasFiles
        {
            get
            {
                return FileInfos != null && FileInfos.Count > 0; 
            }
        }


        //Need to add a way to pull just the directory list. That would be pretty ba. i will just return a list of DirectoryInfo's and pull them out of the files using a list of paths + unique them. should be fairly fast. 

        public IList<String> DirectoryFilters { get; set; } 


        public FileManagerModel() {
            if (Properties.Settings.Default.DirectoryFilters == null)
                Properties.Settings.Default.DirectoryFilters = new StringCollection(); 

            DirectoryFilters = new BindableCollection<String>(Properties.Settings.Default.DirectoryFilters.Cast<String>().ToList());   
        }

        public void SaveDirectoryFilters()
        {
            Properties.Settings.Default.DirectoryFilters.Clear();
            DirectoryFilters.ForEach(x => Properties.Settings.Default.DirectoryFilters.Add(x));
            Properties.Settings.Default.Save(); 
        }
        public int AddFiles(string path, bool recurse = true)
        {
            var getFiles = new GetFileInfosFromDirectoryRoutine(path, recurse);
            getFiles.Execute(null);
            if (getFiles.Results != null)
            {
                getFiles.Results.ForEach(x => {
                    if (!FileInfos.Contains(x))
                        this.FileInfos.Add(x);
                }); 
                return getFiles.Results.Count();
            }
            else
            {
                return 0;
            }
        }
        public void AddFilesAsync(string path, bool recurse = true, Action<object, ResultCompletionEventArgs> onCompleted = null)
        {
            var getFiles = new GetFileInfosFromDirectoryRoutine(path, recurse);

            Caliburn.Micro.Coroutine.BeginExecute(new[] { getFiles as IResult }.Cast<IResult>().GetEnumerator(), null, (x, y) =>
            {
                onCompleted(x, y);
            });
        }

        public List<FileInfo> GetFilesByExtension(string ext)
        {
            if (String.IsNullOrEmpty(ext))
                throw new ArgumentException("String is null or empty");

            if (ext[0] == '.')
                ext = ext.TrimStart(new char[] { '.' });

            if (String.IsNullOrWhiteSpace(ext))
                throw new ArgumentException("Empty String or just Dots");

            //add the . back ;)
            var results = this.FileInfos.Where((x) => {

                if (!x.FileExtension.Extension.Equals("." + ext))
                {
                    return false; 
                }

                foreach(String pattern in this.DirectoryFilters){
                    if (Regex.Match(x.Path, pattern).Success == true)
                        return false;
                }

                return true;
                
            });

            if (results != null)
                return results.ToList();
            else
                return null;
        }

        public List<FileInfo> GetFilesByPath(string path, bool recurse = true)
        {
            if (!System.IO.Directory.Exists(path))
                throw new ArgumentException("Invalid Path!");

            //add the . back ;)
            List<FileInfo> res = null;

            if (recurse)
                res = this.FileInfos.Where((x) => x.Path.StartsWith(path)).ToList();
            else
                res = this.FileInfos.Where((x) => x.Path.Equals(path)).ToList();

            if (res != null)
                return res;
            else
                return null;
        }

        public List<FileInfo> GetFilesByPath(string path, IList<FileExtension> extensions, bool recurse = true)
        {
            List<FileInfo> fileInfos = null; 

            StringBuilder directoryFilter = new StringBuilder();


            if (!this.HasFilesFromDirectory(path))
            {
                this.AddFiles(path, recurse);
            }

            if(DirectoryFilters.Count > 0){
                foreach(string pattern in this.DirectoryFilters){
                    directoryFilter.Append("(" + pattern + ")" + "|");    
                }
                 directoryFilter.Remove(directoryFilter.Length - 1, 1); 
            }

            fileInfos = this.FileInfos.Where((x) => 
            {
                return x.Path.StartsWith(path) && 
                       extensions.Contains(x.FileExtension) &&
                       (String.IsNullOrEmpty(directoryFilter.ToString()) || Regex.Match(x.Path, directoryFilter.ToString()).Success == false);
            }).ToList();
 
            return fileInfos.ToList();

        }

        public bool HasFilesFromDirectory(string path)
        {
            if (!System.IO.Directory.Exists(path))
                throw new ArgumentException("Invalid Path!");

            return this.ContainingPaths.Contains(path); 
        }
    }
}
